/* 
 * File:   Question.cpp
 * Author: mvdr
 * 
 * Created on March 19, 2013, 3:02 PM
 */

#include "Question.h"
#include <algorithm>

using namespace std;

Questions::Questions() :
d_size(20),
d_questionStrings(d_size){
    buildQuestionStrings();
}



Questions::Questions(const Questions& orig) {
}

bool Questions::answer(int player, int question, State const &state) {
    int p1 = (player + 1) % 3;
    int p2 = (player + 2) % 3;
    
    vector<char> vec1 (state.cards(p1).begin(), state.cards(p1).end());
    vector<char> vec2 (state.cards(p2).begin(), state.cards(p2).end());
    
    bool answer;
    
    switch(question)
    {
        case 0:
            answer = areThereConsecutive(vec1, vec2);
            break;
        case 1:
            answer = sumLessThan16(vec1, vec2);
            break;
        case 2:
            answer = sumMoreThan20(vec1, vec2);
            break;
        case 3:
            answer = atLeastOneBoardOnlyEven(vec1, vec2);
            break;
        case 4:
            answer = atLeastOneBoardOnlyOdd(vec1, vec2);
            break;
        case 5:
            answer = onlyEven(vec1, vec2);
            break;
        case 6:
            answer = onlyOdd(vec1, vec2);
            break;
        case 7:
            answer = moreEvenThanOdd(vec1, vec2);
            break;
        case 8:
            answer = moreOddThanEven(vec1, vec2);
            break;
        case 9:
            answer = oddAndEvenEqual(vec1, vec2);
            break;
        case 10:
            answer = areThereConsecutive(vec1, vec2);
            break;
        case 11:
            answer = atLeast1Modulo3(vec1, vec2);
            break;
        case 12:
            answer = atLeast2Modulo3(vec1, vec2);
            break;
        case 13:
            answer = oneNumberDivisableByAnother(vec1, vec2);
            break;
        case 14:
            answer = eachBoardSameSum(vec1, vec2);
            break;
        case 15:
            answer = eachBoardSameDiff(vec1, vec2);
            break;
        case 16:
            answer = fourInARow(vec1, vec2);
            break;
        case 17:
            answer = threeInARow(vec1, vec2);
            break;
        case 18:
            answer = twoInARow(vec1, vec2);
            break;
        case 19:
            answer = atLeast2inARow(vec1, vec2);
            break;
        case 20:
            answer = atLeast3inARow(vec1, vec2);
            break;
        default:
            answer = false;
    }
    
    return answer;
}

bool Questions::areThereConsecutive(vector<char> const &vec1, vector<char> const &vec2) {    
    return abs(vec1[0] - vec1[1]) == 1 || abs(vec2[0] - vec2[1]) == 1;
}

void Questions::printAnswer(int player, int question, bool ans) const
{
    cout << "Player " << player + 1 << " answers her question: ";
    
    if (question == 1)
    {
        cout << "the sum of all numbers she sees is ";
        if (!ans)
            cout << "not ";
        cout << "smaller than 16";
    }
    else if (question == 2)
    {
        cout << "the sum of all numbers she sees is ";
        if (!ans)
            cout << "not ";
        cout << "larger than 20";
    }
    else if (question == 14)
    {
        cout << "the two boards she sees ";
        if (!ans)
            cout << "do not have ";
        cout << "the same sum";                
    }
    else if (question == 15)
    {
        cout << "the two boards she sees ";
        if (!ans)
            cout << "do not have ";
        cout << "the same difference";
    }    
    else
    {
        cout << "she ";
        if (ans)
            cout << "sees ";
        else
            cout << "does not see ";
        cout << d_questionStrings[question];
    }
        
    cout << ".\n\n";
}

bool Questions::atLeastOneBoardOnlyEven(vector<char> const &vec1, vector<char> const &vec2) {
    return ((vec1[0] % 2 == 0) & (vec1[1] % 2 == 0)) ||
            ((vec2[0] % 2 == 0) & (vec2[1] % 2 == 0));
}

bool Questions::atLeastOneBoardOnlyOdd(vector<char> const &vec1, vector<char> const &vec2) {
    return ((vec1[0] % 2 != 0) & (vec1[1] % 2 != 0)) ||
            ((vec2[0] % 2 != 0) & (vec2[1] % 2 != 0));
}

bool Questions::moreEvenThanOdd(vector<char> const &vec1, vector<char> const &vec2) {
    int even = 0;
    int odd = 0;
    
    for (int i = 0; i != 2; ++i)
    {
        if (vec1[i] % 2 == 0)
            ++even;
        else
            ++odd;
        if (vec2[i] % 2 == 0)
            ++even;
        else
            ++odd;
    }
    
    return even > odd;        
}

bool Questions::moreOddThanEven(vector<char> const &vec1, vector<char> const &vec2) {
    int even = 0;
    int odd = 0;
    
    for (int i = 0; i != 2; ++i)
    {
        if (vec1[i] % 2 == 0)
            ++even;
        else
            ++odd;
        if (vec2[i] % 2 == 0)
            ++even;
        else
            ++odd;
    }
    
    return even < odd;        
}

bool Questions::oddAndEvenEqual(vector<char> const &vec1, vector<char> const &vec2) {
        int even = 0;
    int odd = 0;
    
    for (int i = 0; i != 2; ++i)
    {
        if (vec1[i] % 2 == 0)
            ++even;
        else
            ++odd;
        if (vec2[i] % 2 == 0)
            ++even;
        else
            ++odd;
    }
    
    return even == odd;
}

bool Questions::onlyEven(vector<char> const &vec1, vector<char> const &vec2) {
    bool onlyEven = true;
    
    for (int i = 0; i != 2; ++i)
        onlyEven = onlyEven & (vec1[i] % 2 == 0) & (vec2[i] % 2 == 0);
    
    return onlyEven;    
}

bool Questions::onlyOdd(vector<char> const &vec1, vector<char> const &vec2) {
    bool onlyOdd = true;    
    for (int i = 0; i != 2; ++i)
        onlyOdd = onlyOdd & (vec1[i] % 2 != 0) & (vec2[i] % 2 != 0);
    
    return onlyOdd;
}

bool Questions::sumLessThan16(vector<char> const &vec1, vector<char> const &vec2) {
    char sum = 0;
    for (int i = 0; i != 2; ++i)
        sum += vec1[i] + vec2[i];    
    return sum < 16;
}

bool Questions::sumMoreThan20(vector<char> const &vec1, vector<char> const &vec2) {
    char sum = 0;
    for (int i = 0; i != 2; ++i)
        sum += vec1[i] + vec2[i];
    
    return sum > 20;
}

bool Questions::atLeast1Modulo3(std::vector<char> const &p1, std::vector<char> const &p2) {
    std::vector<char> numbers = p1;
    numbers.insert(numbers.end(), p2.begin(), p2.end());
    for(vector<char>::iterator it = numbers.begin(); it != numbers.end(); it++) {
        if (*it % 3 == 0)
                return true;
    }
    return false;
}

bool Questions::atLeast2Modulo3(std::vector<char> const &p1, std::vector<char> const &p2) {
    std::vector<char> numbers = p1;
    numbers.insert(numbers.end(), p2.begin(), p2.end());
    int cnt = 0;
    for(vector<char>::iterator it = numbers.begin(); it != numbers.end(); it++) {
        if (*it % 3 == 0)
                cnt++;
    }
    if (cnt >= 2)
        return true;
    else            
        return false;
}

bool Questions::oneNumberDivisableByAnother(std::vector<char> const &p1, std::vector<char> const &p2) {
    std::vector<char> numbers = p1;
    numbers.insert(numbers.end(), p2.begin(), p2.end());
    int mod2 = 0;
    int mod3 = 0;
    int mod4 = 0;
    bool two = false;
    bool three = false;
    bool four = false;
    for(int i=0; i!=numbers.size(); i++)
    {
        if (numbers[i] % 2 == 0 & numbers[i] != 2)
        {
            if (two)
                return true;
            else
                mod2++;
        }
        if (numbers[i] % 3 == 0 & numbers[i] != 3)
        {
            if (three)
                return true;
            else
                mod3++;
        }
        if (numbers[i] % 4 == 0 & numbers[i] != 4)
        {
            if (four)
                return true;
            else
                mod4++;
        }
        if (numbers[i] == 2)
        {
            if (mod2 > 0)
                return true;
            else
                two = true;
        }
        if (numbers[i] == 3)
        {
            if (mod3 > 0)
                return true;
            else
                three = true;
        }
        if (numbers[i] == 4)
        {
            if (mod4 > 0)
                return true;
            else
                four = true;
        }
    }
    return false;
}

bool Questions::eachBoardSameSum(std::vector<char> const &p1, std::vector<char> const &p2)
{
    if (p1[0]+p1[1] == p2[0] + p2[1])
    {
        return true;
    }
    return false;
}

bool Questions::eachBoardSameDiff(std::vector<char> const &p1, std::vector<char> const &p2)
{
    if (abs(p1[0] - p1[1]) == abs(p2[0] - p2[1]))
    {
        return true;
    }
    return false;
}

bool Questions::fourInARow(std::vector<char> const &p1, std::vector<char> const &p2)
{
    std::vector<char> numbers = p1;
    numbers.insert(numbers.end(), p2.begin(), p2.end());
    sort(numbers.begin(), numbers.end());
    return (numbers[3] - numbers[0] == 3);
}

bool Questions::threeInARow(std::vector<char> const &p1, std::vector<char> const &p2)
{
    std::vector<char> numbers = p1;
    numbers.insert(numbers.end(), p2.begin(), p2.end());
    sort(numbers.begin(), numbers.end());
    
    bool threeInRow = false;
    
    for (int i = 2; i < numbers.size(); i++)
        threeInRow = (numbers[i] - 2 == numbers[i - 2]) != threeInRow;
    
    return threeInRow;
}

bool Questions::twoInARow(std::vector<char> const &p1, std::vector<char> const &p2)
{
    std::vector<char> numbers = p1;
    numbers.insert(numbers.end(), p2.begin(), p2.end());
    sort(numbers.begin(), numbers.end());
    
    bool twoInRow = false;    
    for (int i=1; i < numbers.size(); i++)    
        twoInRow = (numbers[i] - 1 == numbers[i - 1]) != twoInRow;        
    
    return twoInRow;
}


bool Questions::atLeast2inARow(std::vector<char> const &p1, std::vector<char> const &p2)
{
    std::vector<char> numbers = p1;
    numbers.insert(numbers.end(), p2.begin(), p2.end());
    sort(numbers.begin(), numbers.end());
    bool twoInRow = false;
    for (int i=1; i < numbers.size(); i++)
    {
        twoInRow = (numbers[i] - 1 == numbers[i - 1]);
        if (twoInRow)
            break;
    }
    
    return twoInRow;
}

bool Questions::atLeast3inARow(std::vector<char> const &p1, std::vector<char> const &p2)
{
    std::vector<char> numbers = p1;
    numbers.insert(numbers.end(), p2.begin(), p2.end());
    sort(numbers.begin(), numbers.end());
    
    bool threeInRow = false;
    for (int i=2; i < numbers.size(); i++)
    {
        threeInRow = (numbers[i] - 2 == numbers[i - 2]);
        if (threeInRow)
            break;
    }
    
    return threeInRow;
}

void Questions::buildQuestionStrings() {
    d_questionStrings[0] = "boards with consecutive numbers";    
    d_questionStrings[3] = "at least one board with only even numbers";
    d_questionStrings[4] = "at least one board with only odd numbers";
    d_questionStrings[5] = "only even numbers";
    d_questionStrings[6] = "only odd numbers";
    d_questionStrings[7] = "more even numbers than odd numbers";
    d_questionStrings[8] = "more odd numbers than even numbers";
    d_questionStrings[9] = "the same amount of odd and even numbers";
    d_questionStrings[10] = "consecutive numbers on at least one board";
    d_questionStrings[11] = "at least one number which is divisible by 3";
    d_questionStrings[12] = "at least two numbers which are divisible by 3";
    d_questionStrings[13] = "at least two distinct numbers (x,y)\nsuch that x is divisible by y (y can't be 1)";
    d_questionStrings[16] = "exactly four consecutive numbers";
    d_questionStrings[17] = "exactly three consecutive numbers";
    d_questionStrings[18] = "exactly two consecutive numbers";
    d_questionStrings[19] = "at least two consecutive numbers";
}
